module Cloudi
  class ServerSocket
    include Socket::Constants
    
    def initialize(win, port, timer)
      @win = win
      @port = port
      @nick = "Server"   # Server user's Nickname
      @mutex = Mutex.new # For synchronization, so there's no corruption of memory
      @clients = []      # For the sockets that are co
      @timer = timer
      @socket = Socket.new(AF_INET,SOCK_STREAM,0)
      @socket.bind(Socket.sockaddr_in(@port,"localhost"))
      @socket.listen(5)
    end
    
    # MyServerFrame#shutdown()
    # This is the actual clean up function that will shutdown the Server, and
    # disconnect any connected clients.  When this is executed, there should
    # already have a Mutex lock done, so that we can safely clean everything up.
    def shutdown()
      Thread.kill(@thread) unless @thread.nil?
      @timer.stop
    
      @clients.each do |ci|
        ci.sock.send_packet("SHUTDOWN Server is going down now!")
        ci.sock.close
      end
      @clients = []
      @socket.close unless @socket.nil?
      @socket = nil
    end
  
    # MyServerFrame#poll()
    # This function collects the sockets for clients, as well as the server
    # socket to poll for incoming connections or data from these sockets.
    # This returns three arrays of sockets from [read,write,error/oob]
    def poll
      pollers = [@socket]
      @clients.each do |ci|
        pollers << ci.sock
      end
      IO::select(pollers,nil,nil,0.3)
    end
  
    # MyServerFrame#check_server(socks)
    # This is the core of the processing for the server side.  It will check to
    # see if our server socket is in the return for MyServerFrame#poll(), if it's
    # there, then there's an incoming connection, and will run the accepting of
    # the new client connection, and tell the other clients that there is a new
    # incoming connection.  It will then remove the server socket from the array
    # so that it's not processed, as well as converting all the remaining sockets
    # if any, back into ClientInfo structures, there by returning them to be
    # processed by the MyServerFrame#check_clients()
    def check_server(socks)
    
      if socks.index(@socket)
        socks.delete(@socket) # Don't need it in socks
        sock, sockaddr = @socket.accept
        sockaddr = Socket.unpack_sockaddr_in(sockaddr)
        ci = ClientInfo.new("",sockaddr[1],sockaddr[0],sock,"", false)
        @clients.each do |sci|
          puts "CONN #{ci.host} #{ci.port}"
        
          sci.sock.send_packet("CONN #{ci.host} #{ci.port}")
        end
        @clients << ci
       
        puts("[#{Time.now}] - New connection from #{ci.host}:#{ci.port}")
      end
      new_ci = []
      socks.each do |sock|
        @clients.each do |ci|
          if ci.sock == sock
            new_ci << ci
          end
        end
      end
      new_ci
    end
  
    # MyServerFrame#parse_message(ClientInfo,Message)
    # This function parses the incoming data from a Client, to see what is being
    # requested.  There are currently 6 commands that the server implemented.
    # CONNECT identifies the Client with their nickname.
    # NICK identifies the client's new nickname if they change it.
    # EMOTE identifies the message as being an Emote, or Action.
    # MSG identifies the message as being a normal message to the server.
    # WHO is an internal request to update who is connected to the server.
    # QUIT identifies that the client is closing the connection to the server.
    #
    # This function parses the message, and responds appropriately, weither it
    # be an internal message to be handled, or it's a repeater, that needs to be
    # sent to all the connected clients, as well as being displayed to the server.
    def parse_message(ci,msg)
    
      puts "msg-#{msg}"
   
      new_msg = nil
      case msg
      when /CONNECT/
        ports = msg[8..-1]
        
        @win.connect_ports(* ports.split)
        @win.on_paint
      
        ci.sock.send_packet("asdfadsfadsfadsfasdf")
        #ci.name = msg[8..-1]
        #puts("<-> #{ci.name} has joined the server!")
        #new_msg = msg
      when /NICK/
        new_msg = "NICK #{ci.name} #{msg[5..-1]}"
        puts("<-> #{ci.name} has changed nickname to #{msg[5..-1]}")
        ci.name = msg[5..-1]
      when /EMOTE/
        new_msg = "EMOTE #{ci.name} #{msg[6..-1]}"
        puts("<-> #{ci.name} #{msg[6..-1]}")
      when /MSG/
        new_msg = "MSG #{ci.name} #{msg[4..-1]}"
        puts("<-> #{ci.name} says: #{msg[4..-1]}")
      when /WHO/
        who = "WHO #{@nick} "
        @clients.each do |cci|
          who += cci.name + " "
        end
        ci.sock.send_packet(who[0..-2])
      when /QUIT/
        new_msg = "QUIT #{ci.name}"
        puts("<-> #{ci.name} is dis-connecting.")
      else
        puts("<-- Unknown message from #{ci.name}")
        puts("<-- Message: #{msg}")
      end
      @clients.each do |cci|
        if cci != ci
          cci.sock.send_packet(new_msg)
        end
      end unless new_msg.nil?
    end
  
  
  
    # MyServerFrame#check_clients()
    # This method, will run through the collected client connections that show that
    # they have data available in their buffer.  It will first check to see if the
    # socket has been closed by utilizing the IO#eof? method.  If there is an end
    # of file detected, it means the client connection has been closed.  It will then
    # remove the client connection from our pool, and send the rest of the clients
    # that the user has indeed disconnected from the server.
    # After confirming that the connection hasn't been closed, it will check to
    # see what data is available, append it to the client's buffer, where it will
    # then process the data, for the LF (0x0A) termination, saying that this is the
    # end of the message, and then process that data through MyServerFrame#parse_message()
    def check_clients(aci)
      aci.each do |ci|
        if ci.sock.eof?
          ci.sock.close
          @clients.delete(ci)
          @clients.each do |cci|
            cci.sock.send_packet("DISCONN #{ci.name}")
          end
          puts("[#{Time.now}] - Socket closed for #{ci.name}")
        else
          msg = ci.sock.recv_packet()
          ci.buffer += msg
          loop do
            at = ci.buffer.index("\n")
            if at.nil?
              break
            end
            data = ci.buffer[0..at-1]
            ci.buffer = ci.buffer[at+1..-1]
            parse_message(ci,data)
          end
        end
      end
    end
    
    def start
      @thread = Thread.new do
        @timer.start(25)
        loop do # We just run a continious loop.  on_stop() will kill this thread, and stop the timer.
          begin	# We run here, just incase there's some kind of error
            @mutex.lock # We lock the data, so there's no corruption between threads
            socks = poll() # We run a poll for any incoming data, or any new incoming connections
            unless socks.nil?
              ci = check_server(socks[0]) # Check to see if we have any incoming connections
              check_clients(ci)	# Check for client activity
            end
            @mutex.unlock # We can now release it, as we've done everything needed here
          rescue => e  # We have an error, so we need to process it.
            msg = ["An error has occured!"]
            msg << "#{e.backtrace.delete_at(0)}: #{e} (#{e.class})"
            e.backtrace.each do |line|
              msg << line
            end
            msg.each do |line|
              puts(line)
            end
          end
        end
      end
    end
  end
end